#include "Input.h"
#include "GameCommander.h"

const int InputSpeedMod = 40;

// sets member variables to default values
Input::Input()
	: m_pDIObject(NULL)
	, m_pDIKeyboard(NULL)
	, m_pDIMouse(NULL)
{
	for(int i = 0; i < 4; i++)
		m_bMouseButtonDown[i] = false;
}

// initialize object and peripheral devices
bool Input::Init(HWND hWnd, HINSTANCE hInst) {
	// create the direct input object
	DirectInput8Create(hInst, DIRECTINPUT_VERSION, IID_IDirectInput8, (void**)&m_pDIObject, NULL);

	// use that object to create the keyboard and mouse devices
	m_pDIObject->CreateDevice(GUID_SysKeyboard, &m_pDIKeyboard, NULL);
	m_pDIObject->CreateDevice(GUID_SysMouse, &m_pDIMouse, NULL);

	// set data format
	m_pDIKeyboard->SetDataFormat(&c_dfDIKeyboard);
	m_pDIMouse->SetDataFormat(&c_dfDIMouse);

	// set the cooperative level
	m_pDIKeyboard->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);
	m_pDIMouse->SetCooperativeLevel(hWnd, DISCL_FOREGROUND | DISCL_NONEXCLUSIVE);

	// acquire the devices
	HRESULT hr;

	hr = m_pDIKeyboard->Acquire();

	if(FAILED(hr))
		return false;
	
	hr = m_pDIMouse->Acquire();

	if(FAILED(hr))
		return false;

	return true;		// only return true if devices were acquired successfully
}

//Uses Permission to ask if it can go a direction before it does
int Input::CharacterPoll(Character* C,DrawList* drawables,double Speed) {
	HRESULT hr;

	hr = m_pDIKeyboard->GetDeviceState(sizeof(m_kbBuffer), (void*)&m_kbBuffer);

	if(FAILED(hr)) {	
		ZeroMemory(m_kbBuffer, sizeof(m_kbBuffer));

		m_pDIKeyboard->Acquire();		// attempt to re-acquire the device if unable to GetDeviceState
	}

	if(Speed > .1) Speed = .1;

	static float fSpeed[COORD_TYPE_SIZE];
	static D3DXVECTOR3 Prediction(0,0,0);
	float pfSpeed = ((float)Speed * InputSpeedMod);	

	Prediction = C->m_vPosition;
	for(int i = 0;i < COORD_TYPE_SIZE;++i){fSpeed[i] = 0;}


	if(KEYDOWN(m_kbBuffer,DIK_D)) {
		Prediction.x -= pfSpeed;
		if(!C->Collided(drawables,Prediction)) {
			fSpeed [XCOORD_TYPE] += -1 * pfSpeed;
		}
		Prediction.x += pfSpeed;
	}
	if(KEYDOWN(m_kbBuffer,DIK_A)) {
		Prediction.x += pfSpeed;
		if(!C->Collided(drawables,Prediction)) {
			fSpeed [XCOORD_TYPE] += pfSpeed;
		}
		Prediction.x -= pfSpeed;
	}
	if(KEYDOWN(m_kbBuffer,DIK_W)) {
		Prediction.y += pfSpeed;
		if(!C->Collided(drawables,Prediction)) {
			fSpeed [YCOORD_TYPE] += pfSpeed;
		}
		Prediction.y -= pfSpeed;
	}
	if(KEYDOWN(m_kbBuffer,DIK_S)) {
		Prediction.y -= pfSpeed;
		if(!C->Collided(drawables,Prediction)) {
			fSpeed [YCOORD_TYPE] += -1 * pfSpeed;
		}
		Prediction.y += pfSpeed;
	}

//#ifdef _DEBUGGING
	if(KEYDOWN(m_kbBuffer, DIK_C)) {
		if(KEYDOWN(m_kbBuffer, DIK_O)) {
			CLIP = ! CLIP;
			Sleep(150);
		}
		if(KEYDOWN(m_kbBuffer,DIK_U))
		{
			C->CameraMan.EyePos.z = 200;
			C->m_LightRange = 400;
		}
		if(KEYDOWN(m_kbBuffer,DIK_I))
		{
			C->CameraMan.EyePos.z = 60;
			C->m_LightRange = 70;
		}
	}
//#endif

#ifdef IMPLEMENT_SWORD
	if(KEYDOWN(m_kbBuffer, DIK_UP)) {
		C->m_dirAttackDir = NORTH;
	}
	else if(KEYDOWN(m_kbBuffer, DIK_DOWN)) {
		C->m_dirAttackDir = SOUTH;
	}
	else if(KEYDOWN(m_kbBuffer, DIK_LEFT)) {
		C->m_dirAttackDir = WEST;
	}
	else if(KEYDOWN(m_kbBuffer, DIK_RIGHT)) {
		C->m_dirAttackDir = EAST;
	}
	else {
		C->m_dirAttackDir = NullDirection;
	}
#endif

	if(!fSpeed[XCOORD_TYPE] == 0 || !fSpeed[YCOORD_TYPE] == 0)
	{
		C->m_vPosition.x += fSpeed[XCOORD_TYPE];
		C->CameraMan.EyePos.x += fSpeed[XCOORD_TYPE];
		C->CameraMan.LookAt.x += fSpeed[XCOORD_TYPE];
	
		C->m_vPosition.y += fSpeed[YCOORD_TYPE];
		C->CameraMan.EyePos.y += fSpeed[YCOORD_TYPE];
		C->CameraMan.LookAt.y += fSpeed[YCOORD_TYPE];
	}


	return -1;
}

int Input::AIPoll(DrawList* drawables, double Speed) {
	HRESULT hr;

	hr = m_pDIKeyboard->GetDeviceState(sizeof(m_kbBuffer), (void*)&m_kbBuffer);

	if(FAILED(hr)) {	
		ZeroMemory(m_kbBuffer, sizeof(m_kbBuffer));

		m_pDIKeyboard->Acquire();		// attempt to re-acquire the device if unable to GetDeviceState
	}

	if(Speed > .1) Speed = .1;

	static float fSpeed[COORD_TYPE_SIZE];
	static D3DXVECTOR3 Prediction(0,0,0);
	float pfSpeed = ((float)Speed * InputSpeedMod);
	

	for(int i = 0;i < drawables->getSize();++i)
	{
		if(drawables->get(i)->m_Type == ENEMY_TYPE)
		{
			Prediction = drawables->get(i)->m_vPosition;
			for(int j = 0;j < COORD_TYPE_SIZE;++j){fSpeed[j] = 0;}
			switch(((Enemy*)(drawables->get(i)))->GetDirection())
			{
			case AI_RIGHT_DIR:
				Prediction.x -= pfSpeed;
				if(!((Enemy*)(drawables->get(i)))-> Collided(drawables,Prediction)) {
					fSpeed [XCOORD_TYPE] += -.5f * pfSpeed;
				}
				Prediction.x += pfSpeed;
				break;
			case AI_LEFT_DIR:
				Prediction.x += pfSpeed;
				if(!((Enemy*)(drawables->get(i)))->Collided(drawables,Prediction)) {
					fSpeed [XCOORD_TYPE] += +.5f * pfSpeed;
				}
				Prediction.x -= pfSpeed;
				break;
			case AI_UP_DIR:
				Prediction.y += pfSpeed;
				if(!((Enemy*)(drawables->get(i)))->Collided(drawables,Prediction)) {
					fSpeed [YCOORD_TYPE] += +.5f * pfSpeed;
				}
				Prediction.y -= pfSpeed;
				break;
			case AI_DOWN_DIR:
				Prediction.y -= pfSpeed;
				if(!((Enemy*)(drawables->get(i)))->Collided(drawables,Prediction)) {
					fSpeed [YCOORD_TYPE] += -.5f * pfSpeed;
				}
				Prediction.y += pfSpeed;
				break;
			case AI_DIGLFTDW_DIR:
				Prediction.x += pfSpeed;
				Prediction.y -= pfSpeed;
				if(!((Enemy*)(drawables->get(i)))->Collided(drawables,Prediction)) {
					fSpeed [XCOORD_TYPE] += +.5f * pfSpeed;
					fSpeed [YCOORD_TYPE] += -.5f * pfSpeed;
				}
				Prediction.x -= pfSpeed;
				Prediction.y += pfSpeed;
				break;
			case AI_DIGLFTUP_DIR:
				Prediction.x += pfSpeed;
				Prediction.y += pfSpeed;
				if(!((Enemy*)(drawables->get(i)))->Collided(drawables,Prediction)) {
					fSpeed [XCOORD_TYPE] += +.5f * pfSpeed;
					fSpeed [YCOORD_TYPE] += +.5f * pfSpeed;
				}
				Prediction.x -= pfSpeed;
				Prediction.y -= pfSpeed;
				break;
			case AI_DIGRGTUP_DIR:
				Prediction.x -= pfSpeed;
				Prediction.y += pfSpeed;
				if(!((Enemy*)(drawables->get(i)))->Collided(drawables,Prediction)) {
					fSpeed [XCOORD_TYPE] += -.5f * pfSpeed;
					fSpeed [YCOORD_TYPE] += +.5f * pfSpeed;
				}
				Prediction.x += pfSpeed;
				Prediction.y -= pfSpeed;
				break;
			case AI_DIGRGTDW_DIR:
				Prediction.x -= pfSpeed;
				Prediction.y -= pfSpeed;
				if(!((Enemy*)(drawables->get(i)))->Collided(drawables,Prediction)) {
					fSpeed [XCOORD_TYPE] += -.5f * pfSpeed;
					fSpeed [YCOORD_TYPE] += -.5f * pfSpeed;
				}
				Prediction.x += pfSpeed;
				Prediction.y += pfSpeed;
				break;
			default:
				break;
			}
			if(!fSpeed[XCOORD_TYPE] == 0 || !fSpeed[YCOORD_TYPE] == 0)
			{
				drawables->get(i)->m_vPosition.x += fSpeed[XCOORD_TYPE];
				drawables->get(i)->m_vPosition.y += fSpeed[YCOORD_TYPE];
			}
		}
	}

	return -1;
}

int Input::MenuPoll(DrawList* ObjectsSprite) {
	HRESULT hr;

	// get the state of the keyboard
	hr = m_pDIKeyboard->GetDeviceState(sizeof(m_kbBuffer), (void*)&m_kbBuffer);

	if(FAILED(hr)) {	
		ZeroMemory(m_kbBuffer, sizeof(m_kbBuffer));

		m_pDIKeyboard->Acquire();		// attempt to re-acquire the device if unable to GetDeviceState
	}

	
	
	static Menu* MyMenu = NULL;
	if(MyMenu == NULL)
	{
		for(int i = 0;i < ObjectsSprite->getSize();++i)
		{
			if(ObjectsSprite->get(i)->m_Type == MENU_TYPE)
				MyMenu =  ((Menu*)(ObjectsSprite->get(i)));
		}
	}

	for(int i = 0; i < 4; i++) {
		if(BUTTONDOWN(m_mouseState,i))
		{
			if(!m_bMouseButtonDown[i])
			{
				m_bMouseButtonDown[i] = true;
			}
		}

		if(!BUTTONDOWN(m_mouseState,i))
		{
			if(m_bMouseButtonDown[i])
			{
				m_bMouseButtonDown[i] = false;
			}
		}
	}

	// get the state of the mouse
	hr = m_pDIMouse->GetDeviceState(sizeof(m_mouseState), (void*)&m_mouseState);

	static COORD Pos;
	Pos.X = Pos.Y = 0;
	if(BUTTONDOWN(m_mouseState,0) && !m_bMouseButtonDown[0])
	{
		Pos = Cursor::Engine()->Find();
		if(MyMenu->CheckBoundingBoxes(Pos))
		{
			if(MyMenu->ButtonPressed(BUTTON_ONE))
				GAME::Engine()->RESETWORLD();
			if(MyMenu->ButtonPressed(BUTTON_TWO))
				Pen::Engine()->StartCredits();
			if(MyMenu->ButtonPressed(BUTTON_THREE))
				PostQuitMessage(0);
		}
	}

	if(KEYDOWN(m_kbBuffer, DIK_ESCAPE)) {
		return QUIT_STATE;
	}

	if(FAILED(hr)) {
		ZeroMemory(&m_mouseState, sizeof(m_mouseState));

		m_pDIMouse->Acquire();			// attempt to re-acquire the device if unable to GetDeviceState
	}

	return UPDATE_STATE;
}

//Ability to skip
bool Input::MoviePoll()
{
	HRESULT hr;
	hr = m_pDIKeyboard->GetDeviceState(sizeof(m_kbBuffer), (void*)&m_kbBuffer);

	if(FAILED(hr)) {	
		ZeroMemory(m_kbBuffer, sizeof(m_kbBuffer));

		m_pDIKeyboard->Acquire();		// attempt to re-acquire the device if unable to GetDeviceState
	}


	if(KEYDOWN(m_kbBuffer, DIK_RETURN))
	{
		return true;
	}
	return false;
}

float Input::mouseDX()
{return (float)m_mouseState.lX;}
float Input::mouseDY()
{return (float)m_mouseState.lY;}
float Input::mouseDZ()
{return (float)m_mouseState.lZ;}

// release memory
Input::~Input() {
	m_pDIKeyboard->Unacquire();

	m_pDIMouse->Unacquire();

	m_pDIObject->Release();
	
	m_pDIKeyboard->Release();
	
	m_pDIMouse->Release();
}

void Input::CleanUp() {
	m_kbBuffer[256] = NULL;	// captures keyboard input
	m_kbWasHit[256] = NULL;	// records keyboard input
	m_bMouseButtonDown[4] = NULL;
}